package com.idroid.fragments;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Observable;
import java.util.Observer;

import org.osmdroid.DefaultResourceProxyImpl;
import org.osmdroid.ResourceProxy;
import org.osmdroid.api.IGeoPoint;
import org.osmdroid.tileprovider.tilesource.TileSourceFactory;
import org.osmdroid.tileprovider.util.CloudmadeUtil;
import org.osmdroid.util.GeoPoint;
import org.osmdroid.views.MapView;
import org.osmdroid.views.overlay.ItemizedIconOverlay.OnItemGestureListener;
import org.osmdroid.views.overlay.Overlay;
import org.osmdroid.views.overlay.OverlayItem;

import android.content.Intent;
import android.database.SQLException;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Point;
import android.graphics.drawable.Drawable;
import android.location.Address;
import android.location.Geocoder;
import android.location.Location;
import android.os.Bundle;
import android.os.Looper;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.Toast;

import com.idroid.R;
import com.idroid.activities.ParkingDetailActivity;
import com.idroid.database.DatabaseService;
import com.idroid.database.ParkingFlexDataSource;
import com.idroid.domain.AreaType;
import com.idroid.domain.HookableParkingArea;
import com.idroid.domain.LocationTracker;
import com.idroid.domain.ParkingArea;
import com.idroid.domain.User;
import com.idroid.overlays.HookableObjectsOverlay;

public class MapFragment extends Fragment implements
		OnItemGestureListener<OverlayItem>, Observer {

	HookableObjectsOverlay userOverlay;
	HookableObjectsOverlay parkingAreaOverlay;
	MapView mapView;
	static boolean mapViewInitialized = false;
	View v;

	static final double ONE_METER_IN_MILES = 0.000621371192;

	@Override
	public void onResume() {
		// TODO Auto-generated method stub
		super.onResume();
	}

	@Override
	public void onStart() {
		// TODO Auto-generated method stub
		super.onStart();

	}

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);

	}

	private void updateUserLocation(Location location) {
		User user = User.newUser(location.getLatitude(),
				location.getLongitude());
		userOverlay.addOverlayObject(user);
		mapView.postInvalidate();
	}

	@Override
	public View onCreateView(LayoutInflater inflater, ViewGroup container,
			Bundle savedInstanceState) {

		LocationTracker.init(this.getActivity());
		v = inflater.inflate(R.layout.osm_map_view, container, false);

		mapView = (MapView) v.findViewById(R.id.mapview);

		mapView.setClickable(true);
		mapView.setBuiltInZoomControls(true);
		mapView.setDrawingCacheEnabled(true);
		mapView.setMultiTouchControls(true);
		mapView.setTileSource(TileSourceFactory.MAPQUESTOSM);
		CloudmadeUtil.retrieveCloudmadeKey(this.getActivity());

		mapView.getController().setZoom(14);
		Location location = null;
		try {
			location = LocationTracker.getInstance().getCurrentLocation();
		} catch (Exception e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}

		try {
			LocationTracker.getInstance().addObserver(this);
		} catch (Exception e2) {
			// TODO Auto-generated catch block
			e2.printStackTrace();
		}

		Overlay overlay = new Overlay(this.getActivity()) {

			@Override
			protected void draw(Canvas arg0, MapView arg1, boolean arg2) {

			}

			@Override
			public boolean onDoubleTapEvent(MotionEvent event, MapView mapView) {
				System.out.println(event);
				if (event.getAction() == MotionEvent.ACTION_UP) {
					Point touchedPoint = new Point((int) event.getX(),
							(int) event.getY());

					IGeoPoint pt = mapView.getProjection().fromPixels(
							touchedPoint.x, touchedPoint.y);

					Geocoder coder = new Geocoder(
							MapFragment.this.getActivity());

					if (!Geocoder.isPresent()) {
						Toast.makeText(
								MapFragment.this.getActivity(),
								"The emulator you are in does not support GeoCoding.  Please install \"Google APIs\" through the Android SDK Manager and configure a \"Google APIS\" Virtual Device",
								Toast.LENGTH_LONG).show();
					}

					List<Address> addrList = null;
					try {
						addrList = coder.getFromLocation(
								((double) pt.getLatitudeE6() / 1E6),
								((double) pt.getLongitudeE6() / 1E6), 1);
					} catch (IOException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}

					if (addrList != null && addrList.size() > 0) {
						Address address = addrList.get(0);

						String addrString = String.format("%s %s\n  %s, %s %s",
								address.getFeatureName(),
								address.getThoroughfare(),
								address.getLocality(), address.getAdminArea(),
								address.getPostalCode());

						Toast.makeText(
								MapFragment.this.getActivity(),
								"You touched here: \n  "
										+ ((double) pt.getLatitudeE6() / 1E6)
										+ " "
										+ ((double) pt.getLongitudeE6() / 1E6)
										+ "\nClosest Address: \n  "
										+ addrString, Toast.LENGTH_SHORT)
								.show();
					} else {
						Toast.makeText(MapFragment.this.getActivity(),
								"Get out of the water!", Toast.LENGTH_SHORT)
								.show();
					}

				}

				return false;
			}

		};

		ImageButton button = (ImageButton) v.findViewById(R.id.centerButton);

		button.setOnClickListener(new OnClickListener() {

			public void onClick(View v) {

				Location recentLocation = null;
				try {
					recentLocation = LocationTracker.getInstance()
							.getCurrentLocation();
				} catch (Exception e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				}

				User user = User.newUser(recentLocation.getLatitude(),
						recentLocation.getLongitude());
				userOverlay.addOverlayObject(user);

				mapView.getController().setCenter(
						new GeoPoint(recentLocation.getLatitude(),
								recentLocation.getLongitude()));

				mapView.postInvalidate();

				populateNearParkingAreas(parkingAreaOverlay, recentLocation,
						null);

			}
		});

		Drawable defaultMarker = this.getResources().getDrawable(
				R.drawable.androidmarker);
		ResourceProxy proxy = new DefaultResourceProxyImpl(this.getActivity());
		mapView.getOverlays().add(overlay);

		parkingAreaOverlay = new HookableObjectsOverlay(mapView, defaultMarker,
				Color.CYAN, this.getResources(), proxy);

		parkingAreaOverlay.setScaleFactor(.55f);

		parkingAreaOverlay.setOnItemGestureListener(this);

		Bundle extra = this.getActivity().getIntent().getExtras();
		ParkingArea focusedArea = null;
		if (extra != null) {
			focusedArea = (ParkingArea) extra.getSerializable("focusedArea");
			extra.remove("focusedArea");
		}

		try {
			populateNearParkingAreas(parkingAreaOverlay, LocationTracker
					.getInstance().getCurrentLocation(), focusedArea);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		if (focusedArea != null && focusedArea.getHasPosition()) {
			mapView.getController().setCenter(
					new GeoPoint(focusedArea.getLatitude(), focusedArea
							.getLongitude()));
		} else {

			mapView.getController().setCenter(
					new GeoPoint(location.getLatitude(), location
							.getLongitude()));
		}

		mapView.getOverlays().add(parkingAreaOverlay);

		userOverlay = new HookableObjectsOverlay(mapView, defaultMarker,
				this.getResources(), proxy);

		userOverlay.setScaleFactor(.5f);
		User user = User.newUser(location.getLatitude(),
				location.getLongitude());
		userOverlay.addOverlayObject(user);

		mapView.getOverlays().add(userOverlay);

		mapView.invalidate();

		return v;
	}

	@Override
	public void onActivityResult(int requestCode, int resultCode, Intent data) {
		super.onActivityResult(requestCode, resultCode, data);
		if (data != null) {
			Bundle extra = data.getExtras();
			ParkingArea focusedArea = null;
			if (extra != null) {
				focusedArea = (ParkingArea) extra
						.getSerializable("focusedArea");
				extra.remove("focusedArea");
			}

			try {
				if (!focusedArea.getHasPosition()) {
					focusedArea = LocationTracker.getInstance()
							.setAreaPosition(MapFragment.this.getActivity(),
									focusedArea);
				}

				Location l = new Location("");
				l.setLatitude(focusedArea.getLatitude());
				l.setLongitude(focusedArea.getLongitude());

				populateNearParkingAreas(parkingAreaOverlay, l, focusedArea);
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}

			if (focusedArea != null && focusedArea.getHasPosition()) {
				mapView.getController().setCenter(
						new GeoPoint(focusedArea.getLatitude(), focusedArea
								.getLongitude()));
			}

		}
	}

	private HashSet<ParkingArea> displayedParkingAreas = new HashSet<ParkingArea>();

	private void populateNearParkingAreas(
			final HookableObjectsOverlay parkingAreaOverlay,
			final Location location, final ParkingArea focusedArea) {

		if (!Geocoder.isPresent()) {
			Toast.makeText(
					MapFragment.this.getActivity(),
					"The emulator you are in does not support GeoCoding.  Please install \"Google APIs\" through the Android SDK Manager and configure a \"Google APIS\" Virtual Device",
					Toast.LENGTH_LONG).show();
		}

		Thread t = new Thread(new Runnable() {

			public void run() {

				Looper.prepare();

				Geocoder coder = new Geocoder(MapFragment.this.getActivity());

				List<Address> addresses = null;

				try {
					addresses = coder.getFromLocation(location.getLatitude(),
							location.getLongitude(), 1);
				} catch (IOException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				}

				if (addresses != null && addresses.size() > 0) {
					Address localAddress = addresses.get(0);

					List<ParkingArea> dbAreas = DatabaseService.getInstance()
							.getParkingAreasByCitySate(
									MapFragment.this.getActivity(),
									localAddress.getLocality(),
									localAddress.getAdminArea(), false, 0.0,
									false, AreaType.NoInformation);
					//
					// Looper.loop();
					//

					for (ParkingArea area : dbAreas) {
						if (!displayedParkingAreas.contains(area)) {

							if (!area.getHasPosition()) {
								try {
									area = LocationTracker.getInstance()
											.setAreaPosition(
													MapFragment.this
															.getActivity(),
													area);
								} catch (Exception e) {
									// TODO Auto-generated catch block
									e.printStackTrace();
								}

							}

							parkingAreaOverlay
									.addOverlayObjectNoRemove(new HookableParkingArea(
											area, area.getLatitude(), area
													.getLongitude()));

							MapFragment.this.mapView.postInvalidate();
							displayedParkingAreas.add(area);
						}

					}
				}

				if (focusedArea != null) {
					parkingAreaOverlay.setFocusedItem(new HookableParkingArea(
							focusedArea, focusedArea.getLatitude(), focusedArea
									.getLongitude()));
				}

				mapView.postInvalidate();
			}
		});

		t.start();

	}

	public boolean onItemLongPress(int slot, OverlayItem item) {
		if (item instanceof HookableParkingArea) {
			Intent i = new Intent(mapView.getContext(),
					ParkingDetailActivity.class);

			ParkingArea parkingArea = ((HookableParkingArea) item).getArea();

			Location location = null;
			try {
				location = LocationTracker.getInstance().getCurrentLocation();
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			parkingArea = findDistanceFromPoint(location.getLatitude(),
					location.getLongitude(), parkingArea);

			i.putExtra("parkingArea", parkingArea);

			this.startActivityForResult(i, 1);
		}
		return true;
	}

	public boolean onItemSingleTapUp(int slot, OverlayItem item) {
		// TODO Auto-generated method stub
		return false;
	}

	private ParkingArea findDistanceFromPoint(double lat, double lon,
			ParkingArea area) {

		Location userLocation = new Location("");
		userLocation.setLatitude(lat);
		userLocation.setLongitude(lon);

		if (area.getHasPosition()) {
			Location areaLocation = new Location("");
			areaLocation.setLatitude(area.getLatitude());
			areaLocation.setLongitude(area.getLongitude());

			float distanceInMeters = userLocation.distanceTo(areaLocation);

			area.setDistanceFromUserInMiles((float) (ONE_METER_IN_MILES * distanceInMeters));
		}

		return area;

	}

	public void update(Observable observable, Object data) {

		if (data instanceof Location) {
			this.updateUserLocation((Location) data);
		}

	}

}
